/*
 * Copyright (C) 2020 Graylog, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Server Side Public License, version 1,
 * as published by MongoDB, Inc.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Server Side Public License for more details.
 *
 * You should have received a copy of the Server Side Public License
 * along with this program. If not, see
 * <http://www.mongodb.com/licensing/server-side-public-license>.
 */
import * as React from 'react';
import { render, screen, waitFor } from 'wrappedTestingLibrary';
import userEvent from '@testing-library/user-event';

import selectEvent from 'helpers/selectEvent';
import { MockStore } from 'helpers/mocking';
import asMock from 'helpers/mocking/AsMock';
import useFieldTypeMutation from 'views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypeMutation';
import { fetchFieldTypeUsages } from 'views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypeUsages';
import useUserLayoutPreferences from 'components/common/EntityDataTable/hooks/useUserLayoutPreferences';
import { layoutPreferences } from 'fixtures/entityListLayoutPreferences';
import TestStoreProvider from 'views/test/TestStoreProvider';
import useViewsPlugin from 'views/test/testViewsPlugin';
import ChangeFieldTypeModal from 'views/logic/fieldactions/ChangeFieldType/ChangeFieldTypeModal';
import type { Attributes } from 'stores/PaginationTypes';
import useFieldTypesForMappings from 'views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypesForMappings';
import useIndexSetsList from 'components/indices/hooks/useIndexSetsList';
import type { IndexSet } from 'stores/indices/IndexSetsStore';

const onCloseMock = jest.fn();
const renderChangeFieldTypeModal = ({
  onClose = onCloseMock,
  field = 'field',
  show = true,
  showSelectionTable = undefined,
  initialFieldType = undefined,
  initialSelectedIndexSets = ['id-1', 'id-2'],
}) =>
  render(
    <TestStoreProvider>
      <ChangeFieldTypeModal
        onClose={onClose}
        initialData={{
          fieldName: field,
          type: initialFieldType,
        }}
        show={show}
        initialSelectedIndexSets={initialSelectedIndexSets}
        showSelectionTable={showSelectionTable}
      />
    </TestStoreProvider>,
  );
const attributes: Attributes = [
  {
    id: 'index_set_id',
    title: 'Index Set Id',
    type: 'STRING',
    sortable: true,
    hidden: true,
  },
  {
    id: 'index_set_title',
    title: 'Index Set Title',
    type: 'STRING',
    sortable: true,
  },
  {
    id: 'stream_titles',
    title: 'Stream Titles',
    type: 'STRING',
    sortable: false,
  },
  {
    id: 'types',
    title: 'Field Type History',
    type: 'STRING',
    sortable: false,
  },
];

const fieldTypeUsages = [
  {
    id: 'id-1',
    indexSetTitle: 'Index Title 1',
    streamTitles: ['Stream Title 1'],
    types: ['string'],
  },
  {
    id: 'id-2',
    indexSetTitle: 'Index Title 2',
    streamTitles: ['Stream Title 2'],
    types: ['int'],
  },
];
const paginatedFieldUsage = {
  list: fieldTypeUsages,
  pagination: {
    total: 2,
    page: 1,
    perPage: 5,
    count: 1,
  },
  attributes,
};

jest.mock('views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypeMutation', () => jest.fn());
jest.mock('views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypeUsages', () => ({
  ...jest.requireActual('views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypeUsages'),
  fetchFieldTypeUsages: jest.fn(() => async () => {}),
}));
jest.mock('views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypesForMappings', () => jest.fn());
jest.mock('views/logic/fieldactions/ChangeFieldType/hooks/useFieldTypeMutation', () => jest.fn());

jest.mock('components/common/EntityDataTable/hooks/useUserLayoutPreferences');

jest.mock('components/indices/hooks/useIndexSetsList');

jest.mock('stores/indices/IndexSetsStore', () => ({
  IndexSetsActions: {
    list: jest.fn(),
  },
  IndexSetsStore: MockStore([
    'getInitialState',
    () => ({
      indexSets: [
        { id: 'id-1', title: 'Index Title 1' },
        { id: 'id-2', title: 'Index Title 2' },
      ],
    }),
  ]),
}));

describe('ChangeFieldTypeModal', () => {
  const putFieldTypeMutationMock = jest.fn(() => Promise.resolve());

  useViewsPlugin();

  beforeEach(() => {
    asMock(useFieldTypesForMappings).mockReturnValue({
      isLoading: false,
      data: {
        fieldTypes: {
          string: 'String type',
          int: 'Number(int)',
          bool: 'Boolean',
        },
      },
    });

    asMock(useIndexSetsList).mockReturnValue({
      data: {
        indexSets: [
          { id: 'id-1', title: 'Index Title 1' },
          { id: 'id-2', title: 'Index Title 2' },
        ] as Array<IndexSet>,
        indexSetsCount: 2,
        indexSetStats: null,
      },
      isSuccess: true,
      isInitialLoading: false,
      refetch: () => {},
    });

    asMock(useFieldTypeMutation).mockReturnValue({ isLoading: false, putFieldTypeMutation: putFieldTypeMutationMock });
    asMock(fetchFieldTypeUsages).mockResolvedValue(paginatedFieldUsage);

    asMock(useUserLayoutPreferences).mockReturnValue({
      data: {
        ...layoutPreferences,
        attributes: {
          index_set_title: { status: 'show' },
          stream_titles: { status: 'show' },
          types: { status: 'show' },
        },
      },
      isInitialLoading: false,
      refetch: () => {},
    });
  });

  it('Shows rotating indexes', async () => {
    renderChangeFieldTypeModal({});

    await screen.findByText('Rotate affected indices after change');
  });

  it('Shows type options', async () => {
    renderChangeFieldTypeModal({});

    await selectEvent.assertOptionExists('select field type for field', 'Boolean');
  });

  it('Shows index sets data', async () => {
    renderChangeFieldTypeModal({});

    await screen.findByText('Stream Title 1');
    await screen.findByText('Index Title 1');
    await screen.findByText('String type');
    await screen.findByText('Stream Title 2');
    await screen.findByText('Index Title 2');
    await screen.findByText('Number(int)');
  });

  it('run putFieldTypeMutationMock with selected type and indexes', async () => {
    renderChangeFieldTypeModal({});

    await selectEvent.chooseOption('select field type for field', 'Number(int)');

    const submit = await screen.findByTitle(/change field type/i);

    const rowCheckboxes = await screen.findAllByTitle(/deselect entity/i);
    await userEvent.click(rowCheckboxes[1]);
    await userEvent.click(submit);

    await waitFor(() =>
      expect(putFieldTypeMutationMock).toHaveBeenCalledWith({
        indexSetSelection: ['id-1'],
        newFieldType: 'int',
        rotated: true,
        field: 'field',
      }),
    );
  });

  it('run putFieldTypeMutationMock with selected type and indexes when showSelectionTable false', async () => {
    renderChangeFieldTypeModal({ initialSelectedIndexSets: ['id-2'] });

    await selectEvent.chooseOption('select field type for field', 'Number(int)');

    const submit = await screen.findByTitle(/change field type/i);

    await userEvent.click(submit);

    await waitFor(() =>
      expect(putFieldTypeMutationMock).toHaveBeenCalledWith({
        indexSetSelection: ['id-2'],
        newFieldType: 'int',
        rotated: true,
        field: 'field',
      }),
    );
  });

  it("Doesn't shows index sets data when showSelectionTable false", async () => {
    renderChangeFieldTypeModal({ showSelectionTable: false });

    expect(screen.queryByText('Stream Title 1')).not.toBeInTheDocument();
    expect(screen.queryByText('Stream Title 1')).not.toBeInTheDocument();
    expect(screen.queryByText('Index Title 1')).not.toBeInTheDocument();
    expect(screen.queryByText('String type')).not.toBeInTheDocument();
    expect(screen.queryByText('Stream Title 2')).not.toBeInTheDocument();
    expect(screen.queryByText('Index Title 2')).not.toBeInTheDocument();
    expect(screen.queryByText('Number(int)')).not.toBeInTheDocument();
  });

  it('Use initial type', async () => {
    renderChangeFieldTypeModal({ initialFieldType: 'bool' });

    await screen.findByText('Boolean');
  });
});
